/**
 * @描述:
 * @创建人：dongsl
 * @创建时间：20180801
 * @描述：rest功能处理类(项目内接口相互调用工具)
 */
package com.xd.center.http.proxy;

import com.xd.center.http.annotation.HttpHeader;
import com.xd.center.http.annotation.HttpLine;
import com.xd.center.http.authentication.HttpAuth;
import com.xd.center.http.builder.MethodMetadata;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

@Slf4j
public class RestTemplateProxy implements HttpProxy {

  private RestTemplate restTemplate;
  private HttpHeaders defaultHttpHeaders = new HttpHeaders();

  public RestTemplateProxy(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
  }


  @SneakyThrows
  public String auth(String url, Map<String, String> headers, Object requestBean, String key, Long timeout, boolean reset) {
    return null;
  }

  /**
   * post请求
   * requestBean对象
   *
   * @param params 请求参数
   * @return
   */
  @SneakyThrows
  public Object execute(MethodMetadata methodMetadata, Object params[], boolean resetAuth) {
    HttpHeaders headers = getHeader(methodMetadata, resetAuth);
    String url = methodMetadata.getServerMap().get(HttpAuth.URL) + methodMetadata.getUrl();
    StringBuffer logStr = new StringBuffer("调用:").append(url).append("; 请求方式:").append(methodMetadata.getConsumes()).append("; 响应方式:").append(methodMetadata.getProduces());
    try {
      Object request = null;
      if (null != params && params.length > 0) {
        if (HttpHeader.MediaType.JSON == methodMetadata.getConsumes()) {
          request = methodMetadata.encoder(params[0]);
          url = url.replaceAll("\\{key\\}", params[1].toString());
        } else if (HttpHeader.MediaType.TEXT == methodMetadata.getConsumes()) {
          request = params[0];
        } else {
          MultiValueMap<String, Object> requestBodyMap = new LinkedMultiValueMap<>();
          for (int i = 0; i < params.length; i++) {
            Object param = params[i];
            if (null == param) continue;
            if (methodMetadata.getParamNameMap().containsKey(i)) {//通过@HttpParam来获取fieldName
              String fieldName = methodMetadata.getParamNameMap().get(i);
              if (url.indexOf("{" + fieldName + "}") != -1) {
                url = url.replaceAll("\\{" + fieldName + "\\}", param.toString());
                continue;
              }
              requestBodyMap.addAll(fieldName, requestBodyValue(methodMetadata.getConsumes(), param));
            } else {
              Field[] fields = param.getClass().getDeclaredFields();
              for (Field field : fields) {
                String fieldName = field.getName();
                Method getMethod;
                try { //验证方法是否存在
                  getMethod = param.getClass().getDeclaredMethod("get" + firstToUpperCase(fieldName));
                } catch (NoSuchMethodException e) {
                  continue;
                }
                Object value = getMethod.invoke(param);
                if (null == value) continue;
                requestBodyMap.addAll(fieldName, requestBodyValue(methodMetadata.getConsumes(), value));
              }
            }
          }
          request = requestBodyMap;
        }
      }

      Class type = Object.class;
      if (HttpHeader.MediaType.DOWNLOAD == methodMetadata.getProduces()) {
        type = byte[].class;
      } else if (HttpHeader.MediaType.JSON == methodMetadata.getProduces()) {
        type = String.class;
      } else if (HttpHeader.MediaType.TEXT == methodMetadata.getProduces()) {
        type = String.class;
      }

      logStr.append("[状态1:调用开始]");
      ResponseEntity responseEntity = restTemplate.exchange(url, getHttpMethod(methodMetadata.getHttpMethod()), new HttpEntity(request, headers), type);
      logStr.append("[状态2:调用完成]").append("响应类型:").append(responseEntity.getHeaders().getContentType().getSubtype());
      Object result;
      if (MediaType.APPLICATION_JSON.getSubtype().equals(responseEntity.getHeaders().getContentType().getSubtype()) || MediaType.APPLICATION_JSON.equals(getMediaType(methodMetadata.getProduces()))) {
        Object body = responseEntity.getBody();
        if (body instanceof byte[]) body = new String((byte[]) responseEntity.getBody());
        result = methodMetadata.decoder(body);
      } else if (HttpHeader.MediaType.DOWNLOAD == methodMetadata.getProduces()) {
        result = responseEntity;
      } else {
        result = responseEntity.getBody();
      }
      logStr.append("[状态3:解析完成]");
      logStr.append("[返回信息:" + result + "]");
      return result;
    } catch (Exception e) {
      logStr.append("[异常状态:" + e.getMessage() + "]");
      e.printStackTrace();
    } finally {
      log.info(logStr.toString());
    }
    return null;
  }

  private List<Object> requestBodyValue(HttpHeader.MediaType consumes, Object value) throws IOException {
    List<Object> valueList = new ArrayList();

    if (value.getClass().isArray()) {
      valueList.addAll(Arrays.asList(value));
    } else if (value instanceof List) {
      valueList.addAll((Collection<?>) value);
    } else {
      valueList.add(value);
    }
    return valueList;
  }

  public HttpHeaders getHeader(MethodMetadata methodMetadata, boolean resetAuth) {
    HttpHeaders headers = new HttpHeaders();
    MediaType mediaType = getMediaType(methodMetadata.getConsumes());
    if (null != mediaType) {
      headers.setContentType(mediaType);
    }
    for (String hv : methodMetadata.getHeaders()) {
      String[] kv = hv.split(":");
      if (!isEmpty(kv[0]) && !isEmpty(kv[1])) {
        headers.set(kv[0].trim(), kv[1].trim());
      }
    }
    if (null != methodMetadata.getHttpAuth() && methodMetadata.isOpenAuth()) {
      Map<String, String> headerMap = new LinkedHashMap<>();
      methodMetadata.getHttpAuth().auth(methodMetadata, this, headerMap, resetAuth);
      if (!headerMap.isEmpty()) {
        headerMap.entrySet().stream().forEach(entry -> headers.set(entry.getKey(), entry.getValue()));
      }
    }
    return headers;
  }

  public MediaType getMediaType(HttpHeader.MediaType mediaType) {
    if (null != mediaType) {
      MediaType m = MediaType.APPLICATION_OCTET_STREAM;
      if (HttpHeader.MediaType.FORM_URLENCODED == mediaType) {
        m = MediaType.APPLICATION_FORM_URLENCODED;
      } else if (HttpHeader.MediaType.MULTIPART_FORM_DATA == mediaType) {
        m = MediaType.MULTIPART_FORM_DATA;
      } else if (HttpHeader.MediaType.JSON == mediaType) {
        m = MediaType.APPLICATION_JSON;
      } else if (HttpHeader.MediaType.URL_PARAM == mediaType) {
        m = null;
      }
      return m;
    }
    return null;
  }

  public HttpMethod getHttpMethod(HttpLine.Method method) {
    HttpMethod m = HttpMethod.POST;
    if (HttpLine.Method.GET == method) {
      m = HttpMethod.GET;
    } else if (HttpLine.Method.PUT == method) {
      m = HttpMethod.PUT;
    } else if (HttpLine.Method.DELETE == method) {
      m = HttpMethod.DELETE;
    }
    return m;
  }


  public static String firstToUpperCase(String str) {
    char[] chars = str.toCharArray();
    if (chars[0] >= 'a' && chars[0] <= 'z') {
      chars[0] = (char) (chars[0] - 32);
    }
    return String.valueOf(chars);
  }

  public static boolean isEmpty(@Nullable Object str) {
    return str == null || "".equals(str);
  }
}